/**
  * @file cto_prototipo.cpp
  * @brief Fichero que incluye las definiciones de las funciones miembro de la clase cto_prot.
  *
  */
  
  /* Copyright 2010 Antonio Ruano Cuesta
 *
 * This program is free software: you can redistribute it and/or modify it under 
 * the terms of the GNU General Public License as published by the Free Software 
 * Foundation, either version 3 of the License, or (at your option) any later 
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with 
 * this program. If not, see <http://www.gnu.org/licenses/>
*/

#include <new>
#include <fstream>
#include <iostream>
#include "cto_prototipo.h"
#include "prototipo.h"
#include "imagenppm.h"
#include "func_aux.h"

using namespace std;

cto_prot::cto_prot()
{
	num_prototipos=0;
	num_clases=0;
	num_atrib=0;
	prototipos=0;

}

cto_prot::cto_prot(int n_prot, int n_cl, int n_atr)
{
	if (n_prot > 0 && n_cl >= 0 && n_atr > 0){
		num_clases=n_cl;
		num_prototipos=n_prot;
		num_atrib=n_atr;
		prototipos = new (nothrow) prot [n_prot];
	}else
		prototipos = 0;
}

cto_prot::cto_prot(const cto_prot &cprot)
{
	num_prototipos=cprot.num_prototipos;
	num_clases=cprot.num_clases;
	num_atrib=cprot.num_atrib;
	
	prototipos = new (nothrow) prot [num_prototipos];

	for (unsigned int i = 0 ; i<num_prototipos ; i++)
		prototipos[i]=cprot.prototipos[i];
}

cto_prot::~cto_prot()
{
	delete [] prototipos;
}

cto_prot& cto_prot::operator= (const cto_prot &cto)
{
	
	if(this != &cto){
	
		if (num_prototipos > 0)
			delete [] prototipos;
		

		num_prototipos=cto.num_prototipos;
		num_clases=cto.num_clases;
		num_atrib=cto.num_atrib;

		prototipos = new (nothrow) prot [num_prototipos];

		for(unsigned int i = 0 ; i < num_prototipos ; ++i)
			prototipos[i]=cto.prototipos[i];
	}

	return *this;
}

void cto_prot::nuevo_prot (prot prototipo, int pos)
{
	if (prototipo.get_natr() == num_atrib)
		prototipos[pos] = prototipo;
}

prot cto_prot::get_prot (int pos)
{
	return prototipos[pos];
}

unsigned int cto_prot::get_numprot() const
{
	return num_prototipos;
}

void cto_prot::set_numclas(unsigned int nclas)
{
	num_clases = nclas;
}

unsigned int cto_prot::get_numclas() const
{
	return num_clases;
}

unsigned int cto_prot::get_numatrib() const
{
	return num_atrib;
}

void cto_prot::InsertarDesdeFichero (char *fich)
{
	ifstream fi;

	//int nprototipos, natributos, clase;
	int aux;
	float flot;

	fi.open (fich);

	if (!fi){
		cerr<<"Error al abrir el fichero "<<fich<<"."<<endl;
		exit(1);
	}

	fi>>num_prototipos;

	prototipos = new (nothrow) prot [num_prototipos];

	fi>>num_atrib;
			
	prot prototipo (0,num_atrib);			

	fi>>num_clases;
			
	for (unsigned int i = 0 ; i < num_prototipos ; i++){
		fi>>aux;
		prototipo.set_clase(aux);
		for (unsigned int w = 0 ; w < num_atrib ; w++){
			fi>>flot;
			prototipo.set_atrib(flot,w);
		}
		prototipos[i]=prototipo;
	}

	fi.close();
}

void cto_prot::GuardarEnFichero (char *fich)
{

	ofstream fo;
	int clase;
	float flot;

	prot prototipo (0,num_atrib);

	fo.open (fich);

	if (!fo){
		cerr<<"Error al crear el fichero "<<fich<<"."<<endl;
		exit(1);
	}

	
	fo<<num_prototipos<<" "<<num_atrib<<" "<<num_clases<<endl;
		
	for (unsigned int i=0 ; i < num_prototipos ; i++){
		prototipo = prototipos[i];
		clase = prototipo.get_clase();
		fo<<clase<<"\t";
		for (unsigned int j=0 ; j < num_atrib ; j++){
			flot = prototipo.get_atrib(j);
			fo<<flot<<"\t";
		}
		fo<<endl;
	}

	fo.close();
}

void cto_prot::knn (unsigned int k, cto_prot ctoaprend)
{

	float dist_euclid = 0;
	float *distancias = new (nothrow) float [k];
	unsigned int *clases = new (nothrow) unsigned int [k];
	unsigned int clase_mayoritaria, clase;

	num_clases = ctoaprend.get_numclas();

	prot protoclasif (0,num_atrib);

	prot protoaprend (0,num_atrib);

	for ( unsigned int i = 0 ; i < num_prototipos ; i++){

		protoclasif = prototipos[i];

		for (unsigned int j = 0 ; j < ctoaprend.get_numprot() ; j++){

			protoaprend = ctoaprend.get_prot(j);
			dist_euclid = 0;
		
			for (unsigned int w = 0 ; w < ctoaprend.get_numatrib() ; w++){

				dist_euclid += DistEuclidea(protoclasif.get_atrib(w),protoaprend.get_atrib(w));
								   					
			}

			if (j < k){
				distancias[j] = dist_euclid;
				clases[j]=protoaprend.get_clase();

			}
			else{

				clase = protoaprend.get_clase();

				for (unsigned int x = 0 ; x < k ; x++)
					if (dist_euclid < distancias[x]){
						IntercambiarFloat (distancias[x], dist_euclid);
						IntercambiarEnteros (clases[x], clase);
						
					}
			}

		}

		clase_mayoritaria = Moda (clases,k,num_clases);

		protoclasif.set_clase (clase_mayoritaria);

		prototipos[i] = protoclasif;
			
	}

	delete []distancias;
	delete []clases;
	
}

void cto_prot::ClasifMinDisteuclidea (cto_prot ctoaprend)
{

	cto_prot cto_vectores_medios = 	ctoaprend.CalculoCtoVectoresMedios();

	prot protomedio (0,num_atrib);
	prot protoaux (0,num_atrib);
	float dist_euclid,distancia;
	unsigned int clase;

	for (unsigned int i = 0 ; i < num_prototipos ; i++){

		protoaux = prototipos[i];

		for (unsigned int j = 0 ; j < cto_vectores_medios.get_numprot() ; j++){

			protomedio = cto_vectores_medios.get_prot(j);
			dist_euclid = 0;

			for (unsigned int w = 0 ; w < num_atrib ; w++){

				dist_euclid += DistEuclidea(protoaux.get_atrib(w),protomedio.get_atrib(w));
								
			}

			if (j==0){
				distancia = dist_euclid;
				clase = protomedio.get_clase();
			}
			else
				if (dist_euclid < distancia){
					distancia = dist_euclid;
					clase = protomedio.get_clase();
				}

		}

		protoaux.set_clase(clase);

		prototipos[i] = protoaux;
	}

	num_clases = cto_vectores_medios.get_numclas();
		
}

cto_prot cto_prot::CalculoCtoVectoresMedios ()
{
	cto_prot cto_vectores_medios (num_clases, num_clases, num_atrib);
	
	prot protoaux (0,num_atrib);

	float *sumas = new (nothrow) float [num_atrib];
	int contador = 0;
	
	for (unsigned int i = 0 ; i < num_clases ; i++){

		for (unsigned int w = 0 ; w < num_atrib ; w++)
			sumas[w]=0;
		
		contador = 0;
		
		for(unsigned int j = 0 ; j < num_prototipos ; j++){
			
			protoaux = prototipos[j];

			if (protoaux.get_clase() == i+1){				
				for (unsigned int w = 0 ; w < num_atrib ; w++)
					sumas[w] += protoaux.get_atrib(w);
			
				contador++;
			}
					
		}

		protoaux.set_clase (i+1);
	
		for (unsigned int w = 0 ; w < num_atrib ; w++){
			
			sumas[w] = sumas[w] / (float)contador;
			
			protoaux.set_atrib(sumas[w],w);
		}

		cto_vectores_medios.nuevo_prot(protoaux,i);		
	}

	delete [] sumas;

	return cto_vectores_medios;
	
}

int **cto_prot::CalculoMatrizConfusion(cto_prot clasificado)
{
	int **confusion = new (nothrow) int *[num_clases];
		
	for (unsigned int i = 0 ; i < num_clases ; i++)
		confusion[i] = new (nothrow) int [num_clases];

	//Resetear matriz de confusión
	for (unsigned int i = 0 ; i < num_clases ; i++)
		for (unsigned int j = 0 ; j < num_clases ; j++)
			confusion[i][j]=0;

	prot protoaux (0,num_atrib);

	prot prototest (0,num_atrib);

	//Cálculo de la matriz de confusión
	for (unsigned int i = 0 ; i < clasificado.get_numprot() ; i++){
			
		prototest = prototipos[i];
		protoaux = clasificado.get_prot(i);

		confusion[prototest.get_clase()-1][protoaux.get_clase()-1]++;
	}

	return confusion;
}

ImagenPPM cto_prot::ConversionAPPM(int **mapa, int filas, int cols)
{
	ImagenPPM img (filas,cols);
	int contador = 0;
	unsigned char R,G,B;
	unsigned int clase;

	prot protoaux (0, num_atrib);

	for (int i = 0 ; i < filas ; i++){
		
		for (int j = 0 ; j < cols ; j++){

			protoaux = prototipos[contador];
			contador++;
			
			clase = protoaux.get_clase();

			R = mapa [clase-1][0];
			G = mapa [clase-1][1];
			B = mapa [clase-1][2];

			img.set_pixPPM (i,j,R,G,B);		
		}
			
	}

	return img;
}

